/**
 * DatapointHelper.java
 * Copyright (C) 2009 Char Software Inc., DBA Localytics
 * 
 *  This code is provided under the Localytics Modified BSD License.
 *  A copy of this license has been distributed in a file called LICENSE
 *  with this source code.  
 *  
 *  Please visit www.localytics.com for more information.
 */

package com.Localytics.LocalyticsSession;

import net.rim.device.api.synchronization.UIDGenerator;
import net.rim.device.api.i18n.SimpleDateFormat;
import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.system.CoverageInfo;

import java.util.Date; 
import java.util.TimeZone;

/**
 * Provides constants for defining a LocalyticsSession, and any data collection methods
 * which require more than one line of code to retrieve data from the device.
 */
public final class DatapointHelper 
{
    // This class should not be instantiated
    private DatapointHelper() { }
    
    private static final String LOG_PREFIX = "(DatapointHelper) ";
    
    // Each YAML entry either goes to the session controller, the event controller 
    // or the optin controller  
    protected static final String CONTROLLER_SESSION = "- c: se\n";
    protected static final String CONTROLLER_EVENT   = "- c: ev\n";
    protected static final String CONTROLLER_OPT     = "- c: optin\n";
        
    // Each entry is either a create action, or an update action  
    protected static final String ACTION_CREATE      = "  a: c\n";
    protected static final String ACTION_UPDATE      = "  a: u\n";     
    protected static final String ACTION_OPTIN       = "  a: optin\n";
        
    // The target object for the data being set up.
    protected static final String OBJECT_SESSION_DP  = "  se:\n";
    protected static final String OBJECT_EVENT_DP    = "  ev:\n";
    protected static final String OBJECT_OPT         = "  optin:\n";
    
    // Events can have attributes
    public static final String EVENT_ATTRIBUTE    = "   attrs:\n";

     /******************************
     * WEBSERVICE PARAMENTER NAMES *
     ******************************/
    
    // Every object has a UUID
    protected static final String PARAM_UUID            = "u";
    
    // The identifier for this application, generated by the user on the webservice
    protected static final String PARAM_APP_UUID        = "au";
    
    // The version of this application, taken from the application's manifest.
    protected static final String PARAM_APP_VERSION     = "av";
    
    // A session's UUID as previously created.
    protected static final String PARAM_SESSION_UUID    = "su";
    
    // A hashed identifier unique to this device
    protected static final String PARAM_DEVICE_UUID     = "du";
    
    // android, iphone, blackberry, windowsmobile
    protected static final String PARAM_DEVICE_PLATFORM = "dp";
    
    // maker of this device (currently not supported by Android)
    protected static final String PARAM_DEVICE_MAKE     = "dma";
    
    // model of the device
    protected static final String PARAM_DEVICE_MODEL    = "dmo";
    
    // version of the OS on this device
    protected static final String PARAM_OS_VERSION      = "dov";
    
    // country device is from (obtained by querying the SIM card)
    protected static final String PARAM_DEVICE_COUNTRY  = "dc";
    
    // country the current locale is set to
    protected static final String PARAM_LOCALE_COUNTRY  = "dlc";
    
    // country the language is set to
    protected static final String PARAM_LOCALE_LANGUAGE = "dll";
    
    // Locale as a language_country string.  (Not collected because this info 
    //  is already provided by LOCALE_LANGUAGE and LOCALE_COUNTRY.
    protected static final String PARAM_LOCALE          = "dl";
    
    // Country the user is currently in (comes from Sim card)
    protected static final String PARAM_NETWORK_COUNTRY = "nc";
    
    // Current carrier (comes from sim card)
    protected static final String PARAM_NETWORK_CARRIER = "nca";
    
    // Current mobile network code (comes from sim card)
    protected static final String PARAM_NETWORK_MNC     = "mnc";
    
    // current mobile country code (comes from sim card)
    protected static final String PARAM_NETWORK_MCC     = "mcc";
    
    // type of data connection (wifi, umts, gprs, evdo, ...)
    protected static final String PARAM_DATA_CONNECTION = "dac";
    
    // the version of this Localytics client library
    protected static final String PARAM_LIBRARY_VERSION = "lv";
    
    // The source where the location came from
    protected static final String PARAM_LOCATION_SOURCE = "ls";
    
    // the latitude returned by the location provider
    protected static final String PARAM_LOCATION_LAT    = "lat";
    
    // the longitude from the location provider
    protected static final String PARAM_LOCATION_LNG    = "lng";
    
    // the current time on the user's device
    protected static final String PARAM_CLIENT_TIME     = "ct";
    
    // sent at closing time, the current time on the users's device
    protected static final String PARAM_CLIENT_CLOSED_TIME = "ctc";
    
    // The name an event that occurred
    protected static final String PARAM_EVENT_NAME      = "n";   
    
    // the optin value sent in if a user opts in or out. 
    protected static final String PARAM_OPT_VALUE       = "optin";
    
    // Whether the device is configured to use MDS
    protected static final String PARAM_MDS = "blackberry_mds";
    
    /**
     * Returns the given key/value pair as a YAML string, at the specified indentation.
     * @param paramName The name of the parameter 
     * @param paramValue The value of the parameter
     * @param paramIndent The indent level of the parameter
     * @return a YAML string which can be dumped to the YAML file
     */
    protected static String formatYAMLLine(String paramName, String paramValue, int paramIndent)
    {
        if (paramName.length() > LocalyticsSession.MAX_NAME_LENGTH)
        {
            logMessage("Parameter name exceeds " 
                    + LocalyticsSession.MAX_NAME_LENGTH + " character limit.  Truncating.");
            paramName = paramName.substring(0, LocalyticsSession.MAX_NAME_LENGTH);
        }
        if (paramValue.length() > LocalyticsSession.MAX_NAME_LENGTH)
        {
            logMessage("Parameter value exceeds " 
                    + LocalyticsSession.MAX_NAME_LENGTH + " character limit.  Truncating.");
            paramValue = paramValue.substring(0, LocalyticsSession.MAX_NAME_LENGTH);
        }
        // The params are stored in the second tier of the YAML data.
        // so with spacing, the expected result is: "    paramname:paramvalue\n"
        StringBuffer formattedString = new StringBuffer();
        
        for (int currentIndent = 0; currentIndent < paramIndent; currentIndent++)
        {
        	formattedString.append(" ");
        }
        
        formattedString.append(escapeString(paramName));
        formattedString.append(": ");
        
        // Escape the string.
        formattedString.append(escapeString(paramValue));

        formattedString.append("\n");
        
        return formattedString.toString();
    }
    
     /*****************************
     * DATAPOINT COLLECTION UTILS *
     *****************************/
    
    /**
     * Gets the version of a running Blackberry application and returns it. This determines the OS version.
     * @return the version of the operating system
     */
    protected static String getOsVersion()
    {                                        
        // DeviceInfo.getOSVersion returns null on 4.2 so this is the only foolproof way to get OS version
        ApplicationDescriptor[] appDescriptors = ApplicationManager.getApplicationManager().getVisibleApplications();                                
        int numApps = appDescriptors.length;        
        for (int currentApp = numApps-1; currentApp>=0; --currentApp)
        {            
            if ((appDescriptors[currentApp].getModuleName()).equals("net_rim_bb_ribbon_app"))
            {                
                return appDescriptors[currentApp].getVersion();                
            }
        }
        return null;
    }
        
    /**
     * Creates a random string by concatenating a random long to the current time.     
     * @return a randomly generated string which is likely to be unique.
     */
    protected static String generateRandomId()
    {   
        // Generate a random ID (and make sure it is positive).        
        long randomNumber = UIDGenerator.makeLUID(UIDGenerator.getUniqueScopingValue(), UIDGenerator.getUID());
        randomNumber = randomNumber < 0 ? (randomNumber * -1) : randomNumber;
        return Long.toString(randomNumber) + Long.toString(System.currentTimeMillis());        
    }

    /**
     * Creates a DateTime string by getting the current time and passing it, minus the
     * default TimeZone offset to a SimpleDateFormat.
     * @return the current time, in UTC, as a DateTime String
     */
    protected static String getTimeAsDatetime()
    {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        return sdf.format(new Date(System.currentTimeMillis() - TimeZone.getDefault().getRawOffset()));                
    }        
               
    /**
     * Gets the the type of radio network this device is currently using.
     * @return A pretty string for the radio type
     */
    protected static String getNetworkType()
    {
        switch (RadioInfo.getNetworkType())
        {
            // Return any of the valid return types.
            case RadioInfo.NETWORK_802_11 : return "802_11";
            case RadioInfo.NETWORK_CDMA : return "cdma";           
            case RadioInfo.NETWORK_GPRS : return "gprs";
            case RadioInfo.NETWORK_IDEN : return "iden";         
            case RadioInfo.NETWORK_NONE : return "none";
            case RadioInfo.NETWORK_UMTS : return "umts";
        }
  
        return ""; 
    }    
    
    /**
     * Determined whether or not this device is configured to use an MDS server
     * @return true if the device uses MDS, false otherwise.
     */
    protected static String isMDSConfigured()
    {
        return CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS) ? "true" : "false"; 
    }    
       
   /**
     * Escapes strings for YAML parser
     * @param rawString The string we want to escape.
     * @return An escaped string ready for YAML
     */
    private static String escapeString(String rawString)
    {
        StringBuffer parseString = new StringBuffer("\"");
        
        int startRead = 0;       // Index to start reading at
        int stopRead = 0;        // Index characters get read from and where the substring ends
        int bufferLength = rawString.length();
        
        if (rawString == null)
        {
            return "";
        }
        
        // Every time we come across a " or \, append what we have so far, append a \,
        // then manage our indexes to continue where we left off.
        while (stopRead < bufferLength)
        {
            if (rawString.charAt(stopRead) == '\"' || rawString.charAt(stopRead) == '\\')
            {
                parseString.append(rawString.substring(startRead, stopRead));
                parseString.append('\\');
                startRead = stopRead;
            }
            // Skip null characters.
            else if (rawString.charAt(stopRead) == '\0')
            {
                parseString.append(rawString.substring(startRead, stopRead));
                startRead = stopRead + 1;
            }
            stopRead++;
        }
        // Append whatever is left after parsing
        parseString.append(rawString.substring(startRead, stopRead));
        // and finish with a closing "
        parseString.append('\"');
        return parseString.toString();
    }

    /**
     * Prepends a DatapointHelper String and logs a message to the console.
     * @param message Message to log.
     */
    private static void logMessage(String message)
    {
    	System.out.println(DatapointHelper.LOG_PREFIX + message);
    }
} 
